﻿#region Header
//
//	Project:		AllegroNET
//	Description:	Silverlight port of Allegro (http://alleg.sourceforge.net/)
//
//	RLE sprite routines
//
//	Copyright © 2010 Andrey Gayvoronsky
//
//	Please read the License.txt for details.
//
#endregion

using System;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace AllegroNET
{
	public class RLE_SPRITE : IDisposable
	{
		private bool isDisposed = false;
		public int w, h;	// width and height in pixels
		public int size;	// size of sprite data in bytes
		public int[] dat;

		~RLE_SPRITE()
		{
			Dispose(false);
		}

		#region IDisposable Members
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}
		protected virtual void Dispose(bool disposing)
		{
			if (!isDisposed)
			{
				if (disposing)
				{
					dat = null; //Well, actually RLE_SPRITE is pure managed resource now and Dispose is useless right now. But it's better to reserve it to use with destroy_* functions.
				}

				isDisposed = true;
			}
		}
		#endregion
	}

	public partial class Allegro
	{
		static public RLE_SPRITE get_rle_sprite(BITMAP bitmap)
		{
			RLE_SPRITE s;
			int x, y;
			int run;
			int pix;
			int c;

			if (bitmap == null)
				return null;

			c = 0;
			int[] p = new int[bitmap.w * bitmap.h];

			for (y = 0; y < bitmap.h; y++)
			{
				run = -1;
				for (x = 0; x < bitmap.w; x++)
				{
					pix = getpixel(bitmap, x, y) & 0xFFFFFF;

					if (pix != MASK_COLOR_32)
					{
						if ((run >= 0) && (p[run] > 0) && (p[run] < 127))
							p[run]++;
						else
						{
							run = c;

							if (p.Length <= c + bitmap.w)
								Array.Resize(ref p, c + 1024);

							p[c++] = 1;
						}

						if (p.Length <= c + bitmap.w)
							Array.Resize(ref p, c + 1024);

						p[c++] = getpixel(bitmap, x, y);
					}
					else
					{
						if ((run >= 0) && (p[run] < 0) && (p[run] > -128))
							p[run]--;
						else
						{
							run = c;

							if (p.Length <= c + bitmap.w)
								Array.Resize(ref p, c + 1024);

							p[c++] = -1;
						}
					}
				}

				if (p.Length <= c + bitmap.w)
					Array.Resize(ref p, c + 1024);

				p[c++] = MASK_COLOR_32;
			}


			s = new RLE_SPRITE();
			s.w = bitmap.w;
			s.h = bitmap.h;
			s.size = c;
			s.dat = new int[s.size];

			int size_of_rgb = sizeof(int);
			int len = c * size_of_rgb;

			Buffer.BlockCopy(p, 0, s.dat, 0, len);

			return s;
		}

		static public void destroy_rle_sprite(RLE_SPRITE sprite)
		{
			sprite.Dispose();
		}

		static public void draw_rle_sprite(BITMAP dst, RLE_SPRITE src, int dx, int dy)
		{
			int x, y, w, h;
			int dxbeg, dybeg;
			int sxbeg, sybeg;
			int[] s;
			int i;

			if (dst == null || src == null)
				return;

			if (dst.clip)
			{
				int tmp;

				tmp = dst.cl - dx;
				sxbeg = ((tmp < 0) ? 0 : tmp);
				dxbeg = sxbeg + dx;

				tmp = dst.cr - dx;
				w = ((tmp > src.w) ? src.w : tmp) - sxbeg;
				if (w <= 0)
					return;

				tmp = dst.ct - dy;
				sybeg = ((tmp < 0) ? 0 : tmp);
				dybeg = sybeg + dy;

				tmp = dst.cb - dy;
				h = ((tmp > src.h) ? src.h : tmp) - sybeg;
				if (h <= 0)
					return;
			}
			else
			{
				w = src.w;
				h = src.h;
				sxbeg = 0;
				sybeg = 0;
				dxbeg = dx;
				dybeg = dy;
			}

			s = src.dat;
			i = 0;
			// Clip top.
			for (y = sybeg - 1; y >= 0; y--)
			{
				int c = s[i++];

				while (c != MASK_COLOR_32)
				{
					if (c > 0)
						i += c;
					c = s[i++];
				}
			}

			// Visible part.
			if (sxbeg != 0 || dx + src.w >= dst.cr)
			{
				for (y = 0; y < h; y++)
				{
					int d = dst.line[dybeg + y] + dxbeg;
					int c = s[i++];

					// Clip left.
					for (x = sxbeg; x > 0; )
					{
						if (c == MASK_COLOR_32)
							goto next_line;
						else if (c > 0)
						{
							// Run of solid pixels.
							if ((x - c) >= 0)
							{
								// Fully clipped.
								x -= c;
								i += c;
								//s += c;
							}
							else
							{
								// Visible on the right.
								c -= x;
								i += x;
								break;
							}
						}
						else
						{
							// Run of transparent pixels.
							if ((x + c) >= 0)
							{
								// Fully clipped.
								x += c;
							}
							else
							{
								// Visible on the right.
								c += x;
								break;
							}
						}

						c = s[i++];
					}

					// Visible part.
					for (x = w; x > 0; )
					{
						if (c == MASK_COLOR_32)
							goto next_line;
						else if (c > 0)
						{
							// Run of solid pixels.
							if ((x - c) >= 0)
							{
								// Fully visible.
								x -= c;
								for (c--; c >= 0; i++, d++, c--)
								{
									int col = s[i];
									dst.dat[d] = col;
								}
							}
							else
							{
								// Clipped on the right.
								c -= x;
								for (x--; x >= 0; i++, d++, x--)
								{
									int col = s[i];
									dst.dat[d] = col;
								}
								break;
							}
						}
						else
						{
							// Run of transparent pixels.
							x += c;
							d -= c;
						}

						c = s[i++];
					}

					// Clip right.
					while (!(c == MASK_COLOR_32))
					{
						if (c > 0)
							i += c;
						c = s[i++];
					}

				next_line: ;
				}
			}
			else
			{
				for (y = 0; y < h; y++)
				{
					int d = dst.line[dybeg + y] + dxbeg;
					int c = s[i++];

					// Visible part.
					for (x = w; x > 0; )
					{
						if (c == MASK_COLOR_32)
							goto next_line2;
						else if (c > 0)
						{
							// Run of solid pixels.
							if ((x - c) >= 0)
							{
								// Fully visible.
								x -= c;
								for (c--; c >= 0; i++, d++, c--)
								{
									int col = s[i];
									dst.dat[d] = col;
								}
							}
							else
							{
								// Clipped on the right.
								c -= x;
								for (x--; x >= 0; i++, d++, x--)
								{
									int col = s[i];
									dst.dat[d] = col;
								}
								break;
							}
						}
						else
						{
							// Run of transparent pixels.
							x += c;
							d -= c;
						}

						c = s[i++];
					}

				next_line2: ;
				}
			}
		}
	}
}
